/**
 * create by zhang_tian_xiao, 2018/6/11 1:24
 *
 * */
package com.ax.framework.jfinal.db

import com.jfinal.plugin.activerecord.*

/**
 * 给jfinal.com.ax.jshop.model , jfinal.dbpro 扩展函数
 * @author 张天笑
 * @time 2018/11/11 19:37
 *
 */
fun <M : Model<M>> Model<M>.findOneByStrTpl(sql_tpl: String, param: Map<String, Any?> = hashMapOf(), config_name: String = "main"): Model<M>? {
    //切换sql_kit
    val db = Db.use(config_name)
    //
    val sqlPara = db.getSqlParaByString(sql_tpl, param)
    // -> Model, 指定此model对应的数据源
    val me = this.use(config_name)

    return me.findFirst(sqlPara) //as T

    //java中的泛型擦出, 在kt中也无法避免, List<T> 的 T, 拿不到
}

fun <M : Model<M>> Model<M>.findListByStrTpl(sql_tpl: String, param: Map<String, Any?> = hashMapOf(), config_name: String = "main"): List<M> {
    //切换sql_kit
    val db = Db.use(config_name)
    //
    val sqlPara = db.getSqlParaByString(sql_tpl, param)
    // -> Model, 指定此model对应的数据源
    val me = this.use(config_name)

    return me.find(sqlPara)

}


/**
 *@author  小杨
 *@datetime  2018/11/12 0012 12:34
 *
 * */

fun <M : Model<M>> Model<M>.findOneBySqlId(tpl_id: String, param: Map<String, Any?> = hashMapOf(), config_name: String = "main"): M? {
    val sqlPara = this.use(config_name).getSqlPara(tpl_id, param)
    return findFirst(sqlPara)
}


fun <M : Model<M>> Model<M>.findListBySqlId(tpl_id: String, param: Map<String, Any?> = hashMapOf(), config_name: String = "main"): List<M> {
    val sqlPara = this.use(config_name).getSqlPara(tpl_id, param)
    return find(sqlPara)
}

/**
 * Model 所给sql字符串模板执行分页查询
 *
 * @author 张天笑
 * @time 2018/11/11 19:56
 *
 */
fun <M : Model<M>> Model<M>.paginateByStrTpl(sql_tpl: String, page: Int, limit: Int, param: Map<String, Any?> = hashMapOf(), config_name: String = "main"): Page<M> {
    //DbPro
    val db = Db.use(config_name)
    val sqlPara = db.getSqlParaByString(sql_tpl, param)
    return this.dao().paginate(page, limit, sqlPara)
}


fun <M : Model<M>> Model<M>.paginateBySqlId(tpl_id: String, page: Int, limit: Int, param: Map<String, Any?>, config_name: String = "main"): Page<M> {
    //DbPro
    val db = Db.use(config_name)
    val sqlPara = db.getSqlPara(tpl_id, param)
    return this.dao().paginate(page, limit, sqlPara)
}


/**
 * 返回单个Record,
 *@watch 此方法并不会对sql做 limit 1 的处理
 *@author  小杨
 *@datetime  2018/11/12 0012 12:43
 *
 * */
fun DbPro.findOneByStrTpl(sql_tpl: String, param: Map<String, Any?> = hashMapOf()): Record {
    //SqlPara
    val sqlPara = getSqlParaByString(sql_tpl, param)
    // -> Model, 指定此model对应的数据源
    return findFirst(sqlPara)
}

/**
 * 返回List<Record>
 *
 * @author 张天笑
 * @time 2018/11/11 19:50
 *
 */
fun DbPro.findListByStrTpl(sql_tpl: String, param: Map<String, Any?> = hashMapOf()): List<Record> {
    //sqlPara
    val sql_para = getSqlParaByString(sql_tpl, param)
    return this.find(sql_para)
}

/**
 *@param tpl_id sql_id
 *@param param 参数
 *@param config_name 数据源id
 *@author  小杨
 *@datetime  2018/11/12 0012 12:34
 *
 * */

fun DbPro.findOneBySqlId(tpl_id: String, param: Map<String, Any?> = hashMapOf()): Record? {
    val sqlPara = getSqlPara(tpl_id, param)
    return findFirst(sqlPara)
}


/**
 * Db.use(name).findByStrTpl<T> , 根据泛型T 返回所需的值
 *
 * @author 张天笑
 * @time 2018/11/11 19:41
 *
 */
fun DbPro.findListBySqlId(sql_tpl: String, param: Map<String, Any?> = hashMapOf()): List<Record> {
    //sqlPara
    val sql_para = getSqlPara(sql_tpl, param)

    return this.find(sql_para)
}

/**
 *
 *@author  小杨
 *@datetime  2018/11/12 0012 11:56
 * */
inline fun <reified T> DbPro.findBySqlId(tpl_id: String, param: Map<String, Any?> = hashMapOf()): T? {
    //sqlPara
    val sql_para = getSqlPara(tpl_id, param)
    return _find(this, sql_para, param)
}

/**
 *@author  小杨
 *@datetime  2018/11/12 0012 13:04
 *
 * */
inline fun <reified T> DbPro.findByStrTpl(str_tpl: String, param: Map<String, Any?> = hashMapOf()): T? {
    val sql_para = getSqlParaByString(str_tpl, param)
    return _find(this, sql_para, param)
}


/**
 * Db 根据所给sql字符串模板执行分页查询
 *
 * @author 张天笑
 * @time 2018/11/11 19:54
 *
 */
fun DbPro.paginateByStrTpl(sql_tpl: String, page: Int, limit: Int, param: Map<String, Any?> = hashMapOf()): Page<Record> {
    //sqlPara
    val sqlPara = this.getSqlParaByString(sql_tpl, param)
    return paginate(page, limit, sqlPara)
}

/**
 * 此方法利用arp对象中的engine, 解析sql字符串, 返回最终sql和参数列表, 便于与任意持久层框架结合
 *D
 * @author 张天笑
 * @time 2018/11/11 19:30
 *
 */
fun DbPro.getSqlParaByString(sql: String, param: Map<String, Any?>): SqlPara {
    param as HashMap<String, Any?>
    val engine = this.config.sqlKit.engine

    val template = engine.getTemplateByString(sql)
    val sqlPara = SqlPara()
    param["_SQL_PARA_"] = sqlPara
    val string = template.renderToString(param)
    param.remove("_SQL_PARA_")    // 避免污染传入的 Map
    sqlPara.setSql(string)
    return sqlPara
}


fun DbPro.paginateBySqlId(tpl_id: String, page: Int, limit: Int, param: Map<String, Any?>): Page<Record> {
    val sqlPara = getSqlPara(tpl_id, param)
    return paginate(page, limit, sqlPara)
}

inline fun <reified T> _find(dbpro: DbPro, sql_para: SqlPara, param: Map<String, Any?> = hashMapOf()): T? {
    //val queryStr = dbpro.queryStr(sql_para.sql, *sql_para.para)
    val r = when (T::class.simpleName) {
        "String" -> dbpro.queryStr(sql_para.sql, *sql_para.para)
        "Int" -> dbpro.queryInt(sql_para.sql, *sql_para.para)
        "Double" -> dbpro.queryDouble(sql_para.sql, *sql_para.para)
        "Float" -> dbpro.queryFloat(sql_para.sql, *sql_para.para)
        "BigInteger" -> dbpro.queryBigDecimal(sql_para.sql, *sql_para.para).toBigInteger()
        "BigDecimal" -> dbpro.queryBigDecimal(sql_para.sql, *sql_para.para)
        "Record" -> {
            val list = dbpro.queryFirst<T>(sql_para.sql, *sql_para.para)
            list ?: Record()
        }
        else -> {
            throw RuntimeException("查询类型有误")
        }
    }
    return if (r == null) null else r as T
}

/*

inline fun <reified T> t() {
    val clazz_simple_name = T::class.simpleName
    println(clazz_simple_name)
}

fun main(args: Array<String>) {
    //M().findByStrTpl("", hashMapOf("" to ""))
    t<List<String>>()
    t<String>()
    t<Int>()
    t<Double>()
    t<Float>()
    t<BigInteger>()
    t<BigDecimal>()
}
*/